/**
 * enhancer.js JavaScript Library ver0.1
 * http://1000ch.net/
 *
 * Copyright 2012, 1000ch
 * licensed under the MIT license.
 * this is a pale imitation
 **/
( function( window, undefined ) {

	var document = window.document;

	//create scope
	var Enhancer = (function() {

		//easer constructor
		var Enhancer = function( query ) {
			return new Enhancer.fn.query( query );
		};

		//element wrapper class prototype
		Enhancer.fn = Enhancer.prototype = {

			//constructor
			constructor : Enhancer,
			//query selector
			selector : "",

			//select with query
			query : function( querySelector ) {

				//clear
				this.clear();

				//set to this query selector
				this.selector = querySelector;

				//if query selector all is defined
				if( document.querySelectorAll ) {
					//apply selected node list to this
					return this.set( document.querySelectorAll( this.selector ) );
				}
			},

			//cast nodeList to Enhancer object
			set : function( nodeList ) {

				//set element length
				this.length = nodeList.length;

				//set node list to myself
				for( var i = 0;i < this.size();i++ ) {
					this[i] = nodeList[i];
				}

				return this;
			},

			//clear selected node list
			clear : function() {

				//search properties and delete properties
				for( var i = 0;i < this.size();i++ ) {
					delete this[i];
				}

				//reset selector and length
				this.selector = "";
				this.length = 0;

				return this;
			},

			//========================================todo
			//add element
			add : function( querySelector ) {
				//if querySelector is undefined
				if( !querySelector ) {
					return this;
				}
				return this;
			},

			//========================================todo
			//remove element
			remove : function( querySelector ) {
				//if querySelector is undefined
				if( !querySelector ) {
					return this;
				}
				return this;
			},

			//this length
			length : 0,

			//get length of element array
			size : function() {
				return this.length;
			},

			//get or set style
			css : function( key, value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i].style[key];
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						 this[i].style[key] = value;
					}
					return this;
				}
			},

			//get or set attribute
			attr : function( key, value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i].getAttribute( key );
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						this[i].setAttribute( key, value );
					}
					return this;
				}
			},

			//get or set properties
			prop : function( key, value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i][key];
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						this[i][key] = value;
					}
					return this;
				}
			},

			//bind event to elements
			bind : function( trigger, callback ) {
				for( var i = 0;i < this.size();i++ ) {
					Enhancer.events.bind( this[i], trigger, callback );
				}
				return this;
			},

			//unbind event from elements
			unbind : function( trigger, callback ) {
				for( var i = 0;i < this.size();i++ ) {
					Enhancer.events.unbind( this[i], trigger, callback );
				}
				return this;
			},

			//emurate event
			perform : function( trigger ) {
				for( var i = 0;i < this.size();i++ ) {
					Enhancer.events.perform( this[i], trigger );
				}
				return this;
			},

			//get or set value
			val : function( value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i].value;
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						if( this[i].value ) {
							this[i].value = value;
						}
					}
					return this;
				}
			},

			//get or set text
			text : function( value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i].textContent;
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						if( this[i].textContent ) {
							this[i].textContent = value;
						}
					}
					return this;
				}
			},
			
			//get or set html
			html : function( value ) {
				if( value === undefined ) {
					for( var i = 0;i < this.size();i++ ) {
						return this[i].innerHTML;
					}
					return undefined;
				} else {
					for( var i = 0;i < this.size();i++ ) {
						if( this[i].innerHTML ) {
							this[i].innerHTML = value;
						}
					}
				}
			},

			//emurate copy event
			copy : function( callback ) {
				return !callback ? this.perform( "copy" ) : this.bind( "copy", callback );
			},

			//emurate cut event
			cut : function( callback ) {
				return !callback ? this.perform( "cut" ) : this.bind( "cut", callback );
			},

			//emurate paste event
			paste : function( callback ) {
				return !callback ? this.perform( "paste" ) : this.bind( "paste", callback );
			},

			//emurate focus event
			focus : function( callback ) {
				return !callback ? this.perform( "focus" ) : this.bind( "focus", callback );
			},

			//emurate blue event
			blur : function( callback ) {
				return !callback ? this.perform( "blur" ) : this.bind( "blur", callback );
			},

			//emurate change event
			change : function( callback ) {
				return !callback ? this.perform( "change" ) : this.bind( "change", callback );
			},

			//emurate click event
			click : function( callback ) {
				return !callback ? this.perform( "click" ) : this.bind( "click", callback );
			},

			//emurate double click event
			doubleClick : function( callback ) {
				return !callback ? this.perform( "dblclick" ) : this.bind( "dblclick", callback );
			},

			//emurate context menu event
			contextMenu : function( callback ) {
				return !callback ? this.perform( "contextmenu" ) : this.bind( "contextmenu", callback );
			},

			//emurate key down event
			keyDown : function( callback ) {
				return !callback ? this.perform( "keydown" ) : this.bind( "keydown", callback );
			},

			//emurate key press event
			keyPress : function( callback ) {
				return !callback ? this.perform( "keypress" ) : this.bind( "keypress", callback );
			},

			//emurate key up event
			keyUp : function( callback ) {
				return !callback ? this.perform( "keyup" ) : this.bind( "keyup", callback );
			},

			//emurate mouse down event
			mouseDown : function( callback ) {
				return !callback ? this.perform( "mousedown" ) : this.bind( "mousedown", callback );
			},

			//emurate mouse up event
			mouseUp : function( callback ) {
				return !callback ? this.perform( "mouseup" ) : this.bind( "mouseup", callback );
			},

			//emurate mouse over event
			mouseOver : function( callback ) {
				return !callback ? this.perform( "mouseover" ) : this.bind( "mouseover", callback );
			},

			//emurate mouse move event
			mouseMove : function( callback ) {
				return !callback ? this.perform( "mousemove" ) : this.bind( "mousemove", callback );
			},

			//emurate mouse out event
			mouseOut : function( callback ) {
				return !callback ? this.perform( "mouseout" ) : this.bind( "mouseout", callback );
			},

			//emurate resize event
			resize : function( callback ) {
				return !callback ? this.perform( "resize" ) : this.bind( "resize", callback );
			},

			//emurate scroll event
			scroll : function( callback ) {
				return !callback ? this.perform( "scroll" ) : this.bind( "scroll", callback );
			}
		};

		Enhancer.fn.query.prototype = Enhancer.fn;

		return Enhancer;
	})();

	//dom class
	Enhancer.dom = {

		//parse dom to json string
		toJson : function( dom ) {

			//if value is not string
			if( typeof dom === "string" || !dom ) {
				return null;
			}

			//if json class exists
			if( window.JSON && window.JSON.stringify ) {
				return window.JSON.stringify( dom );
			} else {
				return null;
			}
		},

		//parse json string to dom
		fromJson : function( value ) {

			//if value is not string
			if( typeof value !== "string" || !value ) {
				return null;
			}

			//if json class exists
			if( window.JSON && window.JSON.parse ) {
				return window.JSON.parse( value );
			} else {
				return null;
			}
		}
	};

	//element class
	Enhancer.elements = {

		//create dom element
		create : function( tagName, attributes ) {
			//create new element
			var element = document.createElement( tagName );
			//set element
			for( var key in attributes ) {
				element[key] = attributes[key];
			}
			return element;
		},

		//animate element
		//=============================================================================TODO:rewrite
		animate : function( element, timespan, propertyArray ) {
			//if arguments are invalid
			if( !timespan || !propertyArray ) {
				return;
			}

			//get current style
			var style = element.currentStyle ||
				document.defaultView.getComputedStyle( element, null );
			//save current style
			var oldSetting = {};
			for( var key in propertyArray ) {
				oldSetting[key] = style[key];
				if( "width" == key ) {
					oldSetting[key] = element.clientWidth;
				} else if( "height" == key ) {
					oldSetting[key] = element.clientHeight;
				}
			}

			//start time
			var start = new Date();
			//start animation
			var timerId = window.setInterval(
				//execute animation
				function() {
					//current time
					var now = new Date();
					//elapsed time
					var elapsedTime = now - start;
					if( elapsedTime >= timespan ) {
						//if elapsed time is over
						window.clearInterval( timerId );
						//adjust elapsed time
						elapsedTime = timespan;
					}
					
					//old value and new value
					var oldValue;
					var newValue;
					
					//enumerate properties
					for( var key in propertyArray ) {
					
						if( this.numbers.isNumber( oldSetting[key] ) ) {
							//if old value is numeric, cast to float
							oldValue = parseFloat( oldSetting[key] );
						}
						if( this.numbers.isNumber( propertyArray[key] ) ) {
							//if old value is numeric, cast to float
							newValue = parseFloat( propertyArray[key] );
						}
						
						//calcurate difference between new value and old value
						var diff = newValue - oldValue;
						//set style to element
						element.style[key] = newValue - diff * ( ( timespan - elapsedTime ) / timespan );
					}
				},
				//execute by 10ms
				10
			);
			return timerId;
		}
	};

	//number class
	Enhancer.numbers = {

		//judge value is number or not
		isNumber : function( value ) {
			return !isNaN( parseFloat( value ) ) && isFinite( value );
		}
	};

	//string class
	Enhancer.strings = {

		//judge the value is string or not
		isString : function( value ) {
			return ( typeof value == 'string' || value instanceof String );
		},

		//judge the value is empty or not
		isEmpty : function( value ) {
			return !value[0];
		},

		//string format function
		format : function( fmt ) {
			for(var i = 1; i < argments.length; i++){
				fmt = fmt.replace(
					new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
			}
			return fmt;
		},

		//whether value start with suspect string
		startWith : function( value, suspect ) {
			return ( value.match( "^" + suspect ) == suspect );
		},

		//whether value start with suspect string
		endWith : function( value, suspect ) {
			return ( value.match( suspect + "$" ) == suspect );
		},

		//to camel case
		toCamelCase : function( value ) {
			return value.replace(
				/([\-|_][a-z])/g,
				function( m ) {
					return m.substring(1).toUpperCase();
				}
			);
		},

		//to pascal case
		toPascalCase : function( value ) {
			//to camel case
			var buff = this.toCamelCase( value );
			//first charactor to upper
			return buff.replace(
				/(^.)/g,
				function( m ) {
					return m.toUpperCase();
				}
			);
		},

		//to dashed case
		toDashedCase : function( value ) {
			return value.replace(
				/([A-Z])/g,
				function( m ) {
					return "-" + m.toLowerCase();
				}
			);
		},

		//to underscore case
		toUnderscoreCase : function( value ) {
			return value.replace(
				/([A-Z])/g,
				function( m ) {
					return "_" + m.toLowerCase();
				}
			);
		}
	};

	//event class
	Enhancer.events = {

		//add event to element
		bind : function( element, trigger, callback ) {

			//split trigger string combined with whitespace
			var triggerList = trigger.split( " " );

			if( element.addEventListener ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//if addEventListener is defined
					element.addEventListener( triggerList[i].trim(), callback );
				}
			} else if( element.attachEvent ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//if attachEvent is defined
					element.attachEvent( "on" + triggerList[i].trim(), callback );
				}
			}
		},

		//remove event from element
		unbind : function( element, trigger, callback ) {

			//split trigger string combined with whitespace
			var triggerList = trigger.split( " " );

			if( element.removeEventListener ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//if removeEventListener is defined
					element.removeEventListener( triggerList[i].trim(), callback );
				}
			} else if( element.detachEvent ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//if detachEvent is defined
					element.detachEvent( "on" + triggerList[i].trim(), callback );
				}
			}
		},

		//emurate event
		perform : function( element, trigger ) {

			//split trigger string combined with whitespace
			var triggerList = trigger.split( " " );

			if( element.dispatchEvent && document.createEvent ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//create event object and initialize
					var event = document.createEvent("Event");
					event.initEvent( triggerList[i].trim(), true, true );

					//dispatch
					element.dispatchEvent( event );
				}
			} else if( element.fireEvent && document.createEventObject ) {
				for( var i = 0;i < triggerList.length;i++ ) {
					//create event object
					var event = document.createEventObject();

					//dispatch
					element.fireEvent( "on" + triggerList[i].trim(), event );
				}
			}
		},

		//perform or bind copy event
		copy : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "copy" );
			} else {
				this.bind( element, "copy", callback );
			}
		},

		//perform or bind cut event
		cut : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "cut" );
			} else {
				this.bind( element, "cut", callback );
			}
		},

		//perform or bind paste event
		paste : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "paste" );
			} else {
				this.bind( element, "paste", callback );
			}
		},

		//perform or bind focus event
		focus : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "focus" );
			} else {
				this.bind( element, "focus", callback );
			}
		},

		//perform or bind blur event
		blur : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "blur" );
			} else {
				this.bind( element, "blur", callback );
			}
		},

		//perform or bind change event
		change : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "change" );
			} else {
				this.bind( element, "change", callback );
			}
		},

		//perform or bind click event
		click : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "click" );
			} else {
				this.bind( element, "click", callback );
			}
		},

		//perform or bind double click event
		doubleClick : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "dblclick" );
			} else {
				this.bind( element, "dblclick", callback );
			}
		},

		//perform or bind context menu event
		contextMenu : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "contextmenu" );
			} else {
				this.bind( element, "contextmenu", callback );
			}
		},

		//perform or bind key down event
		keyDown : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "keydown" );
			} else {
				this.bind( element, "keydown", callback );
			}
		},

		//perform or bind key press event
		keyPress : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "keypress" );
			} else {
				this.bind( element, "keypress", callback );
			}
		},

		//perform or bind key up event
		keyUp : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "keyup" );
			} else {
				this.bind( element, "keyup", callback );
			}
		},

		//perform or bind mouse down event
		mouseDown : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "mousedown" );
			} else {
				this.bind( element, "mousedown", callback );
			}
		},

		//perform or bind mouse up event
		mouseUp : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "mouseup" );
			} else {
				this.bind( element, "mouseup", callback );
			}
		},

		//perform or bind mouse over event
		mouseOver : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "mouseover" );
			} else {
				this.bind( element, "mouseover", callback );
			}
		},

		//perform or bind mouse move event
		mouseMove : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "mousemove" );
			} else {
				this.bind( element, "mousemove", callback );
			}
		},

		//perform or bind mouse out event
		mouseOut : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "mouseout" );
			} else {
				this.bind( element, "mouseout", callback );
			}
		},

		//perform or bind resize event
		resize : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "resize" );
			} else {
				this.bind( element, "resize", callback );
			}
		},

		//perform or bind scroll event
		scroll : function( element, callback ) {
			if( callback === undefined ) {
				this.perform( element, "scroll" );
			} else {
				this.bind( element, "scroll", callback );
			}
		}
	};

	Enhancer.ajax = {

		//method constant
		method : {
			get : "GET",
			post : "POST"
		},

		//ready state constant
		readyState : {
			unsent : "0",
			opened : "1",
			headersReceived : "2",
			loading : "3",
			done : "4"
		},

		//create xml http request
		create : function() {
			if( window.XMLHttpRequest ) {
				return new window.XMLHttpRequest();
			} else if( window.ActiveXObject ) {
				return new window.ActiveXObject( "Msxml2.XMLHTTP" );
			} else {
				return null;
			}
		},

		//request asyncronous request
		send : function( method, requestPath, params, callbackOnSuccess, callbackOnFailure ) {

			//create xml http request
			var request = this.create();

			try {
				//open request
				request.open( method, requestPath, true );

				//observe state changing
				request.onreadystatechange = function() {

					//this is xml http request object
					//if request is completed
					if( request.readyState == 4 && request.status == 200 ) {
						callbackOnSuccess( request.responseText );
					}
				};

				//send request
				request.send( ( params === undefined ? null : params ) );

			} catch( e ) {
				callbackOnFailure( e );
			}
		},

		//request with get method
		get : function( requestPath, params, callbackOnSuccess, callbackOnFailure ) {

			//send request
			this.send(
				this.method.get, requestPath, params,
				callbackOnSuccess, callbackOnFailure );
		},

		//request with post method
		post : function( requestPath, params, callbackOnSuccess, callbackOnFailure ) {

			//send request
			this.send(
				this.method.post, requestPath, params,
				callbackOnSuccess, callbackOnFailure );
		}
	};

	//set to global object
	window.Enhancer = window.$ = Enhancer;

} )( window );

